ACGAN

ACGAN

背景介绍

  ACGAN(Auxiliary Classifier Generative Adversarial Networks, 辅助分类器生成式对抗网络):于2016年提出,是CGAN类型网络的升级版本,引入了Embedding层对类别标签进行处理,而且增加了类别分类网络,因此称之为辅助分类器生成式对抗网络。

acgan

ACGAN特点

  类似于DCGAN和CGAN的结合,将卷积使用在CGAN网络之中
  引入了Embedding层对类别标签进行处理,Embedding层可以将输入的数字转化为一维向量
  判别器中不但对真假置信度进行loss计算,而且使用了辅助类别分类器,对判别出的类别进行loss计算

ACGAN图像分析

generator
discriminator

TensorFlow2.0实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import os
import numpy as np
import cv2 as cv
from functools import reduce
import tensorflow as tf
import tensorflow.keras as keras


def compose(*funcs):
if funcs:
return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)
else:
raise ValueError('Composition of empty sequence not supported.')


def generator(input_label_shape, input_noise_shape):
label = keras.layers.Input(input_label_shape, name='input_label')
label_tensor = compose(keras.layers.Embedding(10, 100, name='embedding'),
keras.layers.Flatten(name='flatten'))(label)
noise = keras.layers.Input(input_noise_shape, name='input_noise')
x = keras.layers.Multiply(name='multiply')([label_tensor, noise])

x = compose(keras.layers.Dense(1568, activation='relu', name='dense_relu'),
keras.layers.Reshape((7, 7, 32), name='reshape'),
keras.layers.Conv2D(64, (3, 3), (1, 1), 'same', name='conv1'),
keras.layers.BatchNormalization(momentum=0.8, name='bn1'),
keras.layers.ReLU(name='relu1'),
keras.layers.UpSampling2D((2, 2), name='upsampling1'),
keras.layers.Conv2D(128, (3, 3), (1, 1), 'same', name='conv2'),
keras.layers.BatchNormalization(momentum=0.8, name='bn2'),
keras.layers.ReLU(name='relu2'),
keras.layers.UpSampling2D((2, 2), name='upsampling2'),
keras.layers.Conv2D(64, (3, 3), (1, 1), 'same', name='conv3'),
keras.layers.BatchNormalization(momentum=0.8, name='bn3'),
keras.layers.ReLU(name='relu3'),
keras.layers.Conv2D(1, (3, 3), (1, 1), 'same', activation='tanh', name='conv_tanh'))(x)

model = keras.Model([noise, label], x, name='ACGAN-Generator')

return model


def discriminator(input_shape):
input_tensor = keras.layers.Input(input_shape, name='input')
x = input_tensor

x = compose(keras.layers.Conv2D(32, (3, 3), (2, 2), 'same'),
keras.layers.BatchNormalization(momentum=0.8),
keras.layers.LeakyReLU(0.2),
keras.layers.Conv2D(64, (3, 3), (2, 2), 'same'),
keras.layers.BatchNormalization(momentum=0.8),
keras.layers.LeakyReLU(0.2),
keras.layers.Conv2D(128, (3, 3), (2, 2), 'same'),
keras.layers.BatchNormalization(momentum=0.8),
keras.layers.LeakyReLU(0.2),
keras.layers.GlobalAveragePooling2D(name='global_averagepool'))(x)

conf = keras.layers.Dense(1, activation='sigmoid', name='dense_sigmoid')(x)
label = keras.layers.Dense(10, activation='softmax', name='dense_softmax')(x)

model = keras.Model(input_tensor, [conf, label], name='ACGAN-Discriminator')

return model


def acgan(input_label_shape, input_noise_shape, model_g, model_d):
label = keras.layers.Input(input_label_shape, name='input_label')
noise = keras.layers.Input(input_noise_shape, name='input_noise')

x = model_g([noise, label])
model_d.trainable = False
conf, pred_label = model_d(x)

model = keras.Model([noise, label], [conf, pred_label], name='ACGAN')

return model


def save_picture(image, save_path, picture_num):
image = ((image + 1) * 127.5).astype(np.uint8)
image = np.concatenate([image[i * picture_num:(i + 1) * picture_num] for i in range(picture_num)], axis=2)
image = np.concatenate([image[i] for i in range(picture_num)], axis=0)
cv.imwrite(save_path, image)


if __name__ == '__main__':
(x, y), (_, _) = keras.datasets.mnist.load_data()
batch_size = 256
epochs = 20
tf.random.set_seed(22)
save_path = r'.\acgan'
if not os.path.exists(save_path):
os.makedirs(save_path)

x = x[..., np.newaxis].astype(np.float32) / 127.5 - 1
y = y[..., np.newaxis]
x = tf.data.Dataset.from_tensor_slices((x, y)).batch(batch_size)

optimizer = keras.optimizers.Adam(0.0002, 0.5)
loss = keras.losses.BinaryCrossentropy()

real_dacc = keras.metrics.BinaryAccuracy()
fake_dacc = keras.metrics.BinaryAccuracy()
gacc = keras.metrics.BinaryAccuracy()

model_d = discriminator(input_shape=(28, 28, 1))
model_d.compile(optimizer=optimizer, loss=['binary_crossentropy', 'sparse_categorical_crossentropy'])

model_g = generator(input_label_shape=(1,), input_noise_shape=(100,))

model_g.build(input_shape=[(1,), (100,)])
model_g.summary()
keras.utils.plot_model(model_g, 'ACGAN-generator.png', show_shapes=True, show_layer_names=True)

model_d.build(input_shape=(28, 28, 1))
model_d.summary()
keras.utils.plot_model(model_d, 'ACGAN-discriminator.png', show_shapes=True, show_layer_names=True)

model = acgan(input_label_shape=(1,), input_noise_shape=(100,), model_g=model_g, model_d=model_d)
model.compile(optimizer=optimizer, loss=['binary_crossentropy', 'sparse_categorical_crossentropy'])

model.build(input_shape=[(1,), (100,)])
model.summary()
keras.utils.plot_model(model, 'ACGAN.png', show_shapes=True, show_layer_names=True)

for epoch in range(epochs):
x = x.shuffle(np.random.randint(0, 10000))
x_db = iter(x)

for step, (real_image, real_label) in enumerate(x_db):
noise = np.random.normal(0, 1, (real_image.shape[0], 100)).astype(np.float32)
fake_label = np.random.randint(0, 10, (real_image.shape[0], 1))

fake_image = model_g([noise, fake_label])

real_dacc(np.ones((real_image.shape[0], 1)), model_d(real_image)[0])
fake_dacc(np.zeros((real_image.shape[0], 1)), model_d(fake_image)[0])
gacc(np.ones((real_image.shape[0], 1)), model([noise, fake_label])[0])

real_dloss = model_d.train_on_batch(real_image, [np.ones((real_image.shape[0], 1)), real_label])
fake_dloss = model_d.train_on_batch(fake_image, [np.zeros((real_image.shape[0], 1)), fake_label])
gloss = model.train_on_batch([noise, fake_label], [np.ones((real_image.shape[0], 1)), fake_label])

if step % 20 == 0:
print('epoch = {}, step = {}, real_dacc = {}, fake_dacc = {}, gacc = {}'.format(epoch, step, real_dacc.result(), fake_dacc.result(), gacc.result()))
real_dacc.reset_states()
fake_dacc.reset_states()
gacc.reset_states()
fake_data = np.random.normal(0, 1, (100, 100)).astype(np.float32)
fake_label = np.array(list(range(10)) * 10).reshape((-1, 1))
fake_image = model_g([fake_data, fake_label])
save_picture(fake_image.numpy(), save_path + '\\epoch{}_step{}.jpg'.format(epoch, step), 10)

acgan

模型运行结果

acgan

小技巧

  1. 图像输入可以先将其归一化到0-1之间或者-1-1之间,因为网络的参数一般都比较小,所以归一化后计算方便,收敛较快。
  2. 注意其中的一些维度变换和numpytensorflow常用操作,否则在阅读代码时可能会产生一些困难。
  3. 可以设置一些权重的保存方式学习率的下降方式早停方式
  4. ACGAN对于网络结构,优化器参数,网络层的一些超参数都是非常敏感的,效果不好不容易发现原因,这可能需要较多的工程实践经验
  5. 先创建判别器,然后进行compile,这样判别器就固定了,然后创建生成器时,不要训练判别器,需要将判别器的trainable改成False,此时不会影响之前固定的判别器,这个可以通过模型的_collection_collected_trainable_weights属性查看,如果该属性为空,则模型不训练,否则模型可以训练,compile之后,该属性固定,无论后面如何修改trainable,只要不重新compile,都不影响训练。
  6. ACGAN中引入了Embedding层,并且使用乘法将标签结合在输入随机数之中,这样可以避免标签数远远小于随机数的维度造成的灾难,因为CGAN中的生成器是采用Concatenate的方式将其结合,但是如果随机数为100维,而类别只有2类,则类别的影响会非常小。而且采用了卷积层的方式减少了使用全连接层的参数使用辅助分类器对判别器输出的类别标签进行分类,更有效的完成不同类别图像的生成

ACGAN小结

  ACGAN是一种有效的生成式对抗网络,从上图可以看出ACGAN模型的参数量只有0.4M,和DCGAN参数量几乎相同,相比于CGAN,减少了参数量,而且效果有显著的提升,小伙伴们一定要掌握它。

-------------本文结束感谢您的阅读-------------
0%